#include <Ape/String.h>
#include <wchar.h>

#include <stdio.h>
#include <string>

#include <Ape/EIndexOutOfRange.h>
#include <Ape/ENotImplemented.h>

#include <Ape/Collections/CollectionHelpers.h>
#include <Ape/Collections/LinkedList.h>
#include <Ape/Collections/Queue.h>
#include <Ape/Collections/StringList.h>
#include <Ape/IO/Console.h>

namespace Ape {

	size_t String_Util::wcstr_len (const wchar_t* wcstr, size_t maxLen) {
		if (!wcstr)
			return -1u;
		for (size_t i = 0u; i < maxLen || maxLen == -1u; i++) {
			wchar_t ch = wcstr[i];
			if (ch == L'\0')
				return i;
		}
		return maxLen;
	}
	
	size_t String_Util::cstr_len (const char* cstr, size_t maxLen) {
		if (!cstr)
			return -1u;
		for (size_t i = 0u; i < maxLen || maxLen == -1u; i++) {
			char ch = cstr[i];
			if (ch == '\0')
				return i;
		}
		return maxLen;
	}
	
	size_t String_Util::wcstr_calc_allocate (size_t iAtLeast, int sizeOfElement) {
		/*
		size_t iNeededSize = iAtLeast * sizeOfElement;
		size_t iAllocate = _APE_STRING_MIN_BYTES_ALLOCATE;
		for (iAllocate = _APE_STRING_MIN_BYTES_ALLOCATE; iAllocate < iNeededSize; iAllocate *= 2);
		//iAllocate *= 2;
		
		return iAllocate;
		*/
		return Ape::Collections::Helper::CalcAllocateSize (iAtLeast, sizeOfElement);
	}
	
	bool String_Util::wchar2uglychars (wchar_t wch, char* chs) {
		unsigned char* _chs = (unsigned char*) & wch;
		for (size_t i = 0u; i < sizeof (wchar_t); i++) {
			chs[i] = (
						 _chs[i] ?
						 (
							 _chs[i] >= (unsigned char) 0x20 && _chs[i] <= (unsigned char) 0x7f ?
							 _chs[i]
							 : '?'
						 )
								 :
								 '.'
							 );
		}
		chs[sizeof (wchar_t) ] = '\0';
		return chs[1] != '.';
	}
	
	wchar_t String_Util::char2wchar (char ch) {
		wchar_t result = L'\0';
		( (char*) &result) [0] = ch;
		return result;
	}
	
	String::String() :
			m_WCStr (NULL),
			m_Length (0u),
			m_Allocated (0u)
	{
	}
	
	String::~String() {
		//printf("%p\n", m_WCStr);
		del_a (m_WCStr);
	}
	
	void String::Allocate (size_t iLength) {
		if (m_Allocated < iLength) {
			wchar_t* toDelete = m_WCStr;
			m_Allocated = String_Util::wcstr_calc_allocate (iLength, sizeof (wchar_t) ) / sizeof (wchar_t);
			m_WCStr = new wchar_t[m_Allocated];
			del_a (toDelete);
		}
	}
	
	String::String (const wchar_t* wcstr) :
			m_WCStr (NULL),
			m_Length (0u),
			m_Allocated (0u)
	{
		m_Length = String_Util::wcstr_len (wcstr);
		Allocate (m_Length);
		
		for (size_t i = 0u; i < m_Length; i++) {
			m_WCStr[i] = wcstr[i];
		}
	}
	String::String (const wchar_t* wcstr, size_t length) :
			m_WCStr (NULL),
			m_Length (0u),
			m_Allocated (0u)
	{
		//printf("String::ctor - %i\n", length);
		m_Length = length;
		Allocate (m_Length);
		
		for (size_t i = 0u; i < m_Length; i++) {
			if ( (m_WCStr[i] = wcstr[i]) == L'\0') {
				//printf("break - NULL\n");
				m_Length = i;
				break;
			}
		}
	}
	
	String::String (const char* cstr) :
			m_WCStr (NULL),
			m_Length (0u),
			m_Allocated (0u)
	{
		m_Length = String_Util::cstr_len (cstr);
		Allocate (m_Length);
		
		for (size_t i = 0u; i < m_Length; i++) {
			m_WCStr[i] = String_Util::char2wchar (cstr[i]);
		}
	}
	
	String::String (const String& other) :
			Object (other),
			m_WCStr (NULL),
			m_Length (0u),
			m_Allocated (0u)
	{
		m_Length = other.m_Length;
		Allocate (m_Length);
		
		for (size_t i = 0u; i < m_Length; i++) {
			m_WCStr[i] = other.m_WCStr[i];
		}
		
		if (this == &other) {
			printf ("Hellll!!!!!\n");
		}
	}
	
	String& String::operator = (const String& other) {
		//m_WCStr(NULL),
		//m_Length(0u),
		//m_Allocated(0u)
		
		// clean up here
		if (this == &other) {
			printf ("Hellll!!!!!\n");
		}
		
		m_Length = other.m_Length;
		Allocate (m_Length);
		
		for (size_t i = 0u; i < m_Length; i++) {
			m_WCStr[i] = other.m_WCStr[i];
		}
		return *this;
	}
	
	const wchar_t* String::ToWChars() const {
		return m_WCStr;
	}
	
	void String::_Dump (bool verbose) const {
		Object::_Dump (verbose);
		
		DUMP_NON_V_TYPE;
		size_t iLength = get_Length();
		size_t iAllocated = get_Allocated();
		
		printf ("\t\tLength:\t\t%i chars\n", int (iLength) );
		printf ("\t\tAllocated:\t%i chars [%i bytes]\n", int (iAllocated), int (iAllocated * sizeof (wchar_t) ) );
		
		if (verbose) {
			printf ("\t\tChars:\n");
			for (size_t i = 0u; i < iLength; i++) {
				printf ("\t\t\t%i\t=> 0x%08x", int (i), m_WCStr[i]);
				char chs[sizeof (wchar_t) + 1];
				String_Util::wchar2uglychars (m_WCStr[i], chs);
				printf (" [%s]\n", chs);
			}
		}
	}
	
	size_t String::get_Length() const {
		return m_Length;
	}
	size_t String::get_Allocated() const {
		return m_Allocated;
	}
	
	bool String::ToLatin1 (char** pBuff) const {
		char*& buff = *pBuff;
		// release
		del_a (buff);
		// allocate
		size_t iLength = get_Length();
		size_t iAllocate = String_Util::wcstr_calc_allocate (iLength + 1);
		buff = new char[iAllocate];
		// copy
		bool atLeastOneIsNonLatin = false;
		for (size_t i = 0u; i < iLength; i++) {
			char chs[sizeof (wchar_t) + 1];
			bool notLatin = String_Util::wchar2uglychars (m_WCStr[i], chs);
			char ch = notLatin ? '?' : chs[0u];
			buff[i] = ch;
			if (notLatin)
				atLeastOneIsNonLatin = true;
		}
		buff[iLength] = '\0';
		return atLeastOneIsNonLatin;
	}
	
	int String::IndexOf (const Character& needle, size_t from) const {
		size_t iLength = get_Length();
		for (size_t i = from; i < iLength; i++) {
			if (needle == m_WCStr[i])
				return int (i);
		}
		
		return -1;
	}
	
	const String String::Substring (int from, int to) const {
		int ownLength = get_Length();
		if (to == -1)
			to = ownLength;
		if (
			from > ownLength - 1
			|| from > to
			|| to > ownLength
		)
			return L"";
			
		return Substr (to - from, from);
	}
	
	const String String::Substr (size_t length, size_t from) const {
		size_t ownLength = get_Length();
		if (from > ownLength - 1)
			return L"";
		if (from + length > ownLength) {
			length = ownLength - from;
		}
		return String (m_WCStr + from, length);
	}
	
	Character String::At (size_t iPos) const {
		if (iPos >= get_Length() )
#ifndef WITH_EXCEPTIONS
			return L'\0';
#else
			throw EIndexOutOfRange();
#endif
		return m_WCStr[iPos];
	}
	
	bool String::IsEmpty() const {
		return get_Length() == 0;
	}
	
	wchar_t String::WcharAt (size_t iPos) const {
		return At (iPos).C();
	}
	
	const String String::ToString() const {
		return *this;
	}
	
	String::operator const wchar_t* () const {
		return ToWChars();
	}
	
	int String::Find (const String& needle, size_t from) const {
		int len = get_Length();
		int needleLen = needle.get_Length();
		
		if (needleLen == 0 || needleLen > len + (int) from)
			return -1;
			
		int iNextPosToCheck = from;
		
		do {
			iNextPosToCheck = IndexOf (needle.At (0), iNextPosToCheck);
			if (iNextPosToCheck < 0)
				break;
				
			String candidate = Substr (needleLen, iNextPosToCheck);
			if (candidate == needle)
				return iNextPosToCheck;
				
			iNextPosToCheck++;
		} while (iNextPosToCheck >= 0);
		
		return -1;
	}
	
	String String::Replace (const String& what, const String& with) const {
		Collections::StringList parts;
		Split(what, parts);
		/*foreach (part, Collections::StringList, parts) {
			Ape::IO::StdOut << L"> " << (String)part << Ape::IO::NewLine;
		}*/
		return parts.Join(with);
	}
	
	void String::Split (const String& splitter, Collections::StringList& result, int maxParts) const {
		result.Clear();
		int head = -1;
		int tail = Find (splitter);
		for (;;) {
			if (maxParts != -1 && result.get_Count() == (size_t) (maxParts - 1) )
				tail = -1;
			if (head < tail && tail != 0) {
				result << Substring (head + 1, tail);
			}
			else if (tail == -1) {
				String lastPiece = Substring (head + 1);
				if (!lastPiece.IsEmpty() )
					result << lastPiece;
				break;
			}
			head = tail + splitter.get_Length() - 1;
			tail = Find (splitter, tail + 1);
		}
	}
	
	void String::Split (const Character& splitter, Collections::StringList& result, int maxParts) const {
		result.Clear();
		int head = -1;
		int tail = IndexOf (splitter);
		for (;;) {
			if (maxParts != -1 && result.get_Count() == (size_t) (maxParts - 1) )
				tail = -1;
			if (head < tail && tail != 0) {
				result << Substring (head + 1, tail);
			}
			else if (tail == -1) {
				String lastPiece = Substring (head + 1);
				if (!lastPiece.IsEmpty() )
					result << lastPiece;
				break;
			}
			head = tail;
			tail = IndexOf (splitter, tail + 1);
		}
	}
	
	const String operator + (const String& s1, const String& s2) {
		size_t iLength1 = s1.get_Length();
		size_t iLength2 = s2.get_Length();
		size_t iLength =  iLength1 + iLength2;
		
		String result = L"";
		result.Allocate (iLength);
		result.m_Length = iLength;
		
		for (size_t i = 0u; i < iLength1; i++) {
			result.m_WCStr[i] = s1.m_WCStr[i];
		}
		for (size_t i = 0u; i < iLength2; i++) {
			result.m_WCStr[i + iLength1] = s2.m_WCStr[i];
		}
		
		return result;
	}
	
	bool operator != (const String& s1, const String& s2) {
		return ! (s1 == s2);
	}
	
	bool operator == (const String& s1, const String& s2) {
		size_t iL1 = s1.get_Length();
		size_t iL2 = s2.get_Length();
		if (iL1 != iL2)
			return false;
			
		for (size_t i = 0u; i < iL1; i++) {
			if (s1.m_WCStr[i] != s2.m_WCStr[i])
				return false;
		}
		return true;
	}
	
	bool operator > (const String& s1, const String& s2) {
		size_t iL1 = s1.get_Length();
		size_t iL2 = s2.get_Length();
		
		for (size_t i = 0u; i < iL1 && i < iL2; i++) {
			wchar_t wch1 = s1.m_WCStr[i];
			wchar_t wch2 = s2.m_WCStr[i];
			
			if (wch1 == wch2)
				continue;
				
			return wch1 > wch2;
		}
		
		return iL1 > iL2;
	}
	bool operator < (const String& s1, const String& s2) {
		size_t iL1 = s1.get_Length();
		size_t iL2 = s2.get_Length();
		
		for (size_t i = 0u; i < iL1 && i < iL2; i++) {
			wchar_t wch1 = s1.m_WCStr[i];
			wchar_t wch2 = s2.m_WCStr[i];
			
			if (wch1 == wch2)
				continue;
				
			return wch1 < wch2;
		}
		
		return iL1 < iL2;
	}
	
	bool operator >= (const String& s1, const String& s2) {
		size_t iL1 = s1.get_Length();
		size_t iL2 = s2.get_Length();
		
		for (size_t i = 0u; i < iL1 && i < iL2; i++) {
			wchar_t wch1 = s1.m_WCStr[i];
			wchar_t wch2 = s2.m_WCStr[i];
			
			if (wch1 > wch2)
				return true;
			if (wch1 == wch2)
				continue;
		}
		
		return iL1 > iL2;
	}
	bool operator <= (const String& s1, const String& s2) {
		size_t iL1 = s1.get_Length();
		size_t iL2 = s2.get_Length();
		
		for (size_t i = 0u; i < iL1 && i < iL2; i++) {
			wchar_t wch1 = s1.m_WCStr[i];
			wchar_t wch2 = s2.m_WCStr[i];
			
			if (wch1 < wch2)
				return true;
			if (wch1 == wch2)
				continue;
		}
		
		return iL1 < iL2;
	}
	
	Character::Character() : m_WChar (L'\0') {}
	Character::Character (wchar_t wchar) : m_WChar (wchar) {}
	Character::Character (const Character& other)
			: Object (other),
			m_WChar (other.m_WChar) {}
			
	Character::~Character() {}
	
	wchar_t Character::C() const {
		return m_WChar;
	}
	
	/* // It causes a lot of ambiguity
	Character::operator wchar_t() const {
		return m_WChar;
	}
	*/
	
	bool operator != (const Character& c1, const Character& c2) {
		return ! (c1 == c2);
	}
	bool operator == (const Character& c1, const Character& c2) {
		return c1.m_WChar == c2.m_WChar;
	}
	bool operator > (const Character& c1, const Character& c2) {
		return c1.m_WChar > c2.m_WChar;
	}
	bool operator < (const Character& c1, const Character& c2) {
		return c1.m_WChar < c2.m_WChar;
	}
	bool operator >= (const Character& c1, const Character& c2) {
		return c1.m_WChar >= c2.m_WChar;
	}
	bool operator <= (const Character& c1, const Character& c2) {
		return c1.m_WChar <= c2.m_WChar;
	}
}
